package com.leo.boot.web.exception;

import com.leo.boot.web.vo.ResultVO;
import lombok.experimental.ExtensionMethod;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.validation.BindException;
import org.springframework.validation.ObjectError;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.ResponseStatus;
import org.springframework.web.context.request.WebRequest;
import org.springframework.web.servlet.mvc.method.annotation.ResponseEntityExceptionHandler;

import java.util.List;
import java.util.Objects;
import java.util.stream.Collectors;

import static com.leo.boot.web.vo.ResultStatus.BAD_REQUEST;
import static com.leo.boot.web.vo.ResultStatus.INTERNAL_SERVER_ERROR;

@Slf4j
@ExtensionMethod(ResultVO.class)
@ResponseStatus(HttpStatus.OK)
@ResponseBody
@ControllerAdvice
public class GlobalExceptionHandler extends ResponseEntityExceptionHandler {

    @Override
    protected ResponseEntity<Object> handleMethodArgumentNotValid(
            MethodArgumentNotValidException ex, HttpHeaders headers, HttpStatus status, WebRequest request) {
        log.warn("Invalid request of: {}", request.toString(), ex);
        List<String> error = ex.getBindingResult()
                .getAllErrors()
                .stream()
                .map(ObjectError::getDefaultMessage)
                .filter(Objects::nonNull).collect(Collectors.toList());
        return new ResponseEntity<>(BAD_REQUEST.error(error), HttpStatus.OK);
    }

    @Override
    protected ResponseEntity<Object> handleBindException(
            BindException ex, HttpHeaders headers, HttpStatus status, WebRequest request) {
        log.warn("Invalid request of: {}", request.toString(), ex);
        List<String> error = ex.getBindingResult()
                .getAllErrors()
                .stream()
                .map(ObjectError::getDefaultMessage)
                .filter(Objects::nonNull).collect(Collectors.toList());
        return new ResponseEntity<>(BAD_REQUEST.error(error), HttpStatus.OK);
    }

    @Override
    protected ResponseEntity<Object> handleExceptionInternal(
            Exception ex, Object body, HttpHeaders headers, HttpStatus status, WebRequest request) {
        log.error("Internal Exception: {}", request.toString(), ex);
        return new ResponseEntity<>(INTERNAL_SERVER_ERROR.result(), HttpStatus.OK);
    }

    @ExceptionHandler(BusinessException.class)
    public ResultVO<Void> handleBusinessException(BusinessException ex) {
        log.warn("Business Exception: {}", ex.toString());
        return ex.error(ex.getError());
    }
}
